package com.hailiang.saas.auth.service.impl;

import com.alibaba.fastjson.JSON;
import com.hailiang.saas.auth.common.DefaultUserDetails;
import com.hailiang.saas.auth.service.JwtTokenService;
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.MACVerifier;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jose.jwk.RSAKey;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;
import org.springframework.stereotype.Service;

import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.text.ParseException;
import java.util.Objects;

/**
 * 对称加密指的是使用相同的秘钥来进行加密和解密，如果你的秘钥不想暴露给解密方，考虑使用非对称加密。
 * @author : pangfuzhong
 * @description
 * @date : 2021/9/16 10:50
 */
@Slf4j
@Service
public class JwtTokenServiceImpl implements JwtTokenService {

    @Value("${login_auth_url}")
    private String loginAuthUrl;

    private JwtTokenServiceImpl() {
        RSAKey rsaKey = JwtTokenService.rsaKeyMap.get("RSAKEY");
        if(Objects.isNull(rsaKey)) {
            // 生成RsKey
            this.generateDefaultRSAKey();
        }
    }

    /*    @Override
    public PayloadDto getDefaultPayloadDto() {
        Date now = new Date();
        Date exp = DateUtil.offsetSecond(now, 60*60);
        return PayloadDto.builder()
                .sub("macro")
                .iat(now.getTime())
                .exp(exp.getTime())
                .jti(UUID.randomUUID().toString())
                .username("macro")
                .authorities(Lists.newArrayList("ADMIN"))
                .build();
    }*/

    @Override
    public String generateTokenByHMAC(String payloadStr, String secret) throws JOSEException {
        JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.RS256).type(JOSEObjectType.JWT).build();
        Payload payload = new Payload(payloadStr);
        JWSObject jwsObject = new JWSObject(jwsHeader, payload);
        JWSSigner jwsSigner = new MACSigner(secret);
        jwsObject.sign(jwsSigner);
        return jwsObject.serialize();
    }

    @Override
    public UserDetails verifyTokenByHMAC(String token, String secret) throws ParseException, JOSEException {
        //从token中解析JWS对象
        JWSObject jwsObject = JWSObject.parse(token);
        //创建HMAC验证器
        JWSVerifier jwsVerifier = new MACVerifier(secret);
        if (!jwsObject.verify(jwsVerifier)) {
            throw new RuntimeException("token签名不合法！");
        }
        String payload = jwsObject.getPayload().toString();
        DefaultUserDetails defaultUserDetails = JSON.parseObject(payload, DefaultUserDetails.class);
        /*if (defaultUserDetails.getExp() < new Date().getTime()) {
            throw new RuntimeException("token已过期！");
        }*/
        return null;
    }

    /**
     * 返回非对称加密JWTtoken
     * */
    @Override
    public String generateTokenByRSA(String payloadStr, RSAKey rsaKey) throws JOSEException {
        //创建JWS头，设置签名算法和类型,即Header
        JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.RS256).type(JOSEObjectType.JWT).build();
        //将负载信息封装到Payload中, 即Payload
        Payload payload = new Payload(payloadStr);
        //创建JWS对象
        JWSObject jwsObject = new JWSObject(jwsHeader, payload);
        //创建RSA签名器, 使用RSA非对称进行签名即进行加密,即Signature
        JWSSigner jwsSigner = new RSASSASigner(rsaKey, true);
        //执行签名, 最终形成Header.Payload.Signature为字符串的JWT token
        jwsObject.sign(jwsSigner);
        return jwsObject.serialize();
    }

    @Override
    public Boolean verifyTokenByRSA(String token) throws ParseException, JOSEException {
        try {
            //从token中解析JWS对象
            JWSObject jwsObject = JWSObject.parse(token);
            RSAKey rsaKey = rsaKeyMap.get("RSAKEY");
            //使用RSA公钥创建RSA验证器
            JWSVerifier jwsVerifier = new RSASSAVerifier(rsaKey.toPublicJWK());
            if (!jwsObject.verify(jwsVerifier)) {
                return Boolean.FALSE;
                //throw new RuntimeException("token签名不合法！");
            }
        } catch (Exception e) {
            log.debug("--> JwtTokenServiceImpl: token验证异常, 校验失败");
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    /**
     * 通过KeyPairGenerator生成
     *
     * @return*/
    @Override
    public RSAKey generateDefaultRSAKey() {
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            // 初始化密钥对生成器，密钥大小为96-1024位
            keyPairGenerator.initialize(1024, new SecureRandom());
            // 生成一个密钥对，保存在keyPair中
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();   // 得到私钥
            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();  // 得到公钥
            // 得到公钥字符串
            String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
            // 得到私钥字符串
            String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded())));
            // 将公钥和私钥保存到Map
            RSAKey rsaKey = new RSAKey.Builder(publicKey).privateKey(privateKey).build();
            rsaKeyMap.put("RSAKEY", rsaKey);
            return rsaKey;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 通过jdk自带方式生成RSA公钥私钥对
     * */
    @Override
    public RSAKey getDefaultRSAKey() {
        //从classpath下获取RSA秘钥对
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "hailiang".toCharArray());
        KeyPair keyPair = keyStoreKeyFactory.getKeyPair("jwt", "hailiang".toCharArray());
        //获取RSA公钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        //获取RSA私钥
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        return new RSAKey.Builder(publicKey).privateKey(privateKey).build();
    }

    /**
     * RSA公钥加密
     * @param str 待加密字符串
     * @param publicKey 公钥
     * @return 密文(返回str被publicKey加密后的字符串)
     * @throws Exception 加密过程中的异常信息
     */
    public static String encrypt( String str, String publicKey ) throws Exception{
        //base64编码的公钥
        byte[] decoded = Base64.decodeBase64(publicKey);
        RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
        //RSA加密
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
        String outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes("UTF-8")));
        return outStr;
    }

    /**
     * RSA私钥解密
     * @param str 公钥加密成功后的字符串
     * @param privateKey 私钥
     * @return 铭文 将str通过私钥privateKey进行解密后的原始字符串
     * @throws Exception 解密过程中的异常信息
     */
    public static String decrypt(String str, String privateKey) throws Exception{
        //64位解码加密后的字符串
        byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8"));
        //base64编码的私钥
        byte[] decoded = Base64.decodeBase64(privateKey);
        RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
        //RSA解密
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, priKey);
        String outStr = new String(cipher.doFinal(inputByte));
        return outStr;
    }
}
